#ifndef LINUX_KERNEL_H
#define  LINUX_KERNEL_H

#include <types.h>

static inline uint8_t inb(uint16_t  port) {
	uint8_t rv;
	__asm__ volatile("inb %[p], %[v]" : [v]"=a" (rv) : [p]"d"(port));
	return rv;
}

static inline uint16_t inw(uint16_t  port) {
	uint16_t rv;
	__asm__ volatile("in %1, %0" : "=a" (rv) : "dN" (port));
	return rv;
}

static inline void outb(uint16_t port, uint8_t data) {
	__asm__ volatile("outb %[v], %[p]" : : [p]"d" (port), [v]"a" (data));
}

static inline void outw(uint16_t  port, uint16_t data) {
	__asm__ volatile("outb %[v], %[p]" : : [p]"d" (port), [v]"a" (data));
}

static inline void cli() {
	__asm__ volatile("cli");
}

static inline void sti() {
	__asm__ volatile("sti");
}

static inline void lgdt(uint32_t start, uint32_t size) {
    struct {
		uint16_t limit;
		uint16_t start_l;
		uint16_t start_h;
	} gdt;

	gdt.start_h = start >> 16;
	gdt.start_l = start & 0xFFFF;
	gdt.limit = size - 1;
	__asm__ volatile("lgdt %[g]"::[g]"m"(gdt));
}

static inline void lidt(uint32_t start, uint32_t size) {
    struct {
		uint16_t limit;
		uint16_t start_l;
		uint16_t start_h;
	} idt;

	idt.start_h = start >> 16;
	idt.start_l = start & 0xFFFF;
	idt.limit = size - 1;
	__asm__ volatile("lidt %[g]"::[g]"m"(idt));
}

static inline uint32_t read_cr0() {
	uint32_t cr0;
	__asm__ volatile("mov %%cr0, %[v]":[v]"=r"(cr0));
	return cr0;
}

static inline uint32_t read_cr2() {
	uint32_t cr2;
	__asm__ volatile("mov %%cr2, %[v]":[v]"=r"(cr2));
	return cr2;
}

static inline uint32_t read_cr3() {
	uint32_t cr3;
	__asm__ volatile("mov %%cr3, %[v]":[v]"=r"(cr3));
	return cr3;
}

static inline uint32_t read_cr4() {
	uint32_t cr4;
	__asm__ volatile("mov %%cr4, %[v]":[v]"=r"(cr4));
	return cr4;
}

static inline uint32_t read_eflags() {
	uint32_t eflags;
	__asm__ volatile("pushf;"
		"pop %%eax;":"=a"(eflags));
	return eflags;
}

static inline void write_eflags(uint32_t eflags) {
	__asm__ volatile("push %%eax;"
		"popf;"::"a"(eflags));
}

static inline void write_cr0(uint32_t v) {
	__asm__ volatile("mov %[v], %%cr0"::[v]"r"(v));
}

static inline void write_cr3(uint32_t v) {
	__asm__ volatile("mov %[v], %%cr3"::[v]"r"(v));
}

static inline void write_cr4(uint32_t v) {
	__asm__ volatile("mov %[v], %%cr4"::[v]"r"(v));
}

static inline void far_jump(uint32_t selector, uint32_t offset) {
	uint32_t addr[] = { offset, selector };
	__asm__ volatile("ljmpl *(%[a])"::[a]"r"(addr));
}

static inline void hlt() {
    __asm__ volatile("hlt");
}

static inline void write_tr(uint16_t sel) {
	__asm__ volatile("ltr %%ax"::"a"(sel));
}

void protect_mode();
#endif